package cn.guetzjb.sys.controller;

import cn.guetzjb.core.entity.R;
import cn.guetzjb.security.anotation.RequiresPermissions;
import cn.guetzjb.security.anotation.RequiresRoles;
import cn.guetzjb.security.utils.AuthUtil;
import cn.guetzjb.security.utils.SecurityUtils;
import cn.guetzjb.sys.domain.*;
import cn.guetzjb.sys.service.*;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/role")
@Tag(name = "用户角色")
@RequiredArgsConstructor
public class RoleController {

    private final SysRoleService roleService;
    private final SysUserRoleService userRoleService;
    private final SysRolePermService rolePermService;
    private final SysPermService permService;
    private final SysMenuService menuService;
    private final SysRoleMenuService roleMenuService;

    @GetMapping("/all")
    @RequiresPermissions(value = "role:manage:query")
    @Operation(summary = "获取全部")
    public R all(@RequestParam(required = false, defaultValue = "1") Integer current,
                 @RequestParam(required = false, defaultValue = "10") Integer pageSize,
                 @RequestParam(required = false) String params) {
        return R.ok(roleService.page(new Page<>(current, pageSize)));
    }

    @PostMapping("/update")
    @RequiresPermissions(value = "role:manage:update")
    @Operation(summary = "更新")
    public R update(@RequestBody SysRole sysRole) {
        boolean ok = roleService.updateById(sysRole);
        if (ok) {
            return R.ok();
        }
        return R.failed("更新异常");
    }

    @PostMapping("/delete/{ids}")
    @RequiresPermissions(value = "role:manage:delete")
    @Operation(summary = "删除")
    public R delete(@PathVariable List<Integer> ids) {
        boolean ok = roleService.removeBatchByIds(ids);
        if (ok) {
            return R.ok();
        }
        return R.failed("删除异常");
    }

    @PostMapping("/add")
    @RequiresPermissions(value = "role:manage:add")
    @Operation(summary = "新增")
    public R add(@RequestBody SysRole sysRole) {
        sysRole.setCreateBy(SecurityUtils.getUsername());
        sysRole.setCreateTime(new Date());
        sysRole.setRemark(sysRole.getRoleName());
        boolean ok = roleService.save(sysRole);
        if (ok) {
            return R.ok();
        }
        return R.failed("新增异常");
    }

    @GetMapping("/roleUsers")
    @RequiresPermissions(value = "role:manage:query")
    @Operation(summary = "角色对应的用户", description = "各个角色中有哪些用户")
    public R roleUsers() {
        List<SysRole> roleToUsers = roleService.getRoleToUsers();

        return R.ok(roleToUsers);
    }

    @PostMapping("/removeRoleUsers/{userId}/{roleId}")
    @RequiresPermissions(value = "role:manage:delete")
    @Operation(summary = "移出角色组")
    public R removeRoleUsers(@PathVariable Long userId, @PathVariable Long roleId) {
        if (AuthUtil.isAdmin(userId)) {
            return R.failed("不能移出超级管理员");
        }
        boolean ok = userRoleService.remove(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getUserId, userId).eq(SysUserRole::getRoleId, roleId));
        if (ok) {
            return R.ok();
        }
        return R.failed("用户不属于组或操作异常");
    }

    @PostMapping("/addRoleUsers/{userIds}/{roleId}")
    @RequiresRoles(value = {"admin"})
    @Operation(summary = "分配角色")
    public R addRoleUsers(@PathVariable List<Long> userIds, @PathVariable Long roleId) {
        for (Long userId : userIds) {
            long count = userRoleService.count(Wrappers.<SysUserRole>lambdaQuery()
                    .and(v -> v.eq(SysUserRole::getUserId, userId).eq(SysUserRole::getRoleId, roleId)));
            if (count > 0) {
                continue;
            }
            userRoleService.save(new SysUserRole(userId, roleId));
        }
        return R.ok();
    }

    @PostMapping("/add/auth/{roleId}")
    @RequiresRoles(value = {"admin"})
    @Operation(summary = "角色授权")
    @Transactional
    public R roleAddAuth(@PathVariable Long roleId, @RequestBody List<String> permKeyList) {
        if (roleId == 1L) {
            return R.failed("超级管理员无法变更权限");
        }
        //先删除所有权限
        rolePermService.remove(Wrappers.<SysRolePerm>lambdaQuery().eq(SysRolePerm::getRoleId, roleId));
        if (permKeyList.isEmpty()) {
            return R.ok(null, "权限已清空");
        }
        List<SysRolePerm> list = new ArrayList<>();
        List<SysRolePerm> rolePermList = rolePermService.list();
        Map<Long, Set<String>> rolePermMap = new HashMap<>();
        Set<String> permSet = new HashSet<>() {{
            add("query");
            add("add");
            add("update");
            add("delete");
        }};
        //过滤不对的权限符
        permKeyList = permKeyList.stream().filter(v -> {
            for (String perm : permSet) {
                if (v.endsWith(perm)) {
                    return true;
                }
            }
            return false;
        }).toList();
        for (SysRolePerm rolePerm : rolePermList) {
            rolePermMap.computeIfAbsent(rolePerm.getRoleId(), k -> new HashSet<>()).add(rolePerm.getPermKey());
        }
        for (String permKey : permKeyList) {
            if (!rolePermMap.containsKey(roleId) || !rolePermMap.get(roleId).contains(permKey)) {
                // 角色不存在该权限，添加
                SysRolePerm rolePerm = new SysRolePerm(permKey, roleId);
                list.add(rolePerm);
            }
        }
        //同步角色显示的菜单
        List<String> suffixPerm = permKeyList.stream().map(v -> v.substring(0, v.lastIndexOf(":"))).toList();
        if (!suffixPerm.isEmpty()) {
            List<SysMenu> roleHaveMenuList = menuService.list(Wrappers.<SysMenu>lambdaQuery().in(SysMenu::getMenuPermPrefix, suffixPerm));
            //搜索对应菜单的父类菜单,加入角色允许的权限中
            List<Long> parentMenuIds = roleHaveMenuList.stream().map(SysMenu::getParentId).toList();
            roleHaveMenuList.addAll(menuService.list(Wrappers.<SysMenu>lambdaQuery().in(SysMenu::getMenuId, parentMenuIds)));
            //先删掉用户所有菜单
            roleMenuService.remove(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getRoleId, roleId));
            //构造对象集合
            List<SysRoleMenu> roleHaveMenuIdList = roleHaveMenuList.stream().map(v -> new SysRoleMenu(roleId, v.getMenuId())).toList();
            roleMenuService.saveBatch(roleHaveMenuIdList);
        }
        boolean ok = rolePermService.saveBatch(list);
        return R.ok(null, ok ? "角色授权成功" : "权限保持一致");
    }

    @GetMapping("/auth/tree")
    @Operation(summary = "角色权限树")
    @RequiresPermissions(value = "role:manage:query")
    public R roleAuthTree() {
        List<String> permList = permService.list().stream().map(SysPerm::getPermKey).toList();
        //映射
        Map<String, String> ysMap = new HashMap<>() {{
            put("query", "查询");
            put("add", "新增");
            put("update", "修改");
            put("delete", "删除");
        }};
        Map<String, List<String>> map = new HashMap<>();
        for (String perm : permList) {
            int idx = perm.lastIndexOf(":");
            String leftPermStr = perm.substring(0, idx);
            String rightPermStr = perm.substring(idx + 1);
            map.computeIfAbsent(leftPermStr, v -> new ArrayList<>()).add(ysMap.get(rightPermStr));
        }
        return R.ok(map);
    }

    @GetMapping("/auth/all/{roleId}")
    @Operation(summary = "角色拥有的权限")
    @RequiresPermissions(value = "role:manage:query")
    public R roleAuthAll(@PathVariable Long roleId) {
        List<String> list = rolePermService.list(Wrappers.<SysRolePerm>lambdaQuery().eq(SysRolePerm::getRoleId, roleId))
                .stream().map(SysRolePerm::getPermKey).toList();
        if (list.contains("*")) {
            return R.ok(permService.list().stream().map(SysPerm::getPermKey).toList());
        }
        return R.ok(list);
    }

}
